package cjq.demo.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

//http://developer.51cto.com/art/201304/388411.htm
public class LamdbaDemo {
	
	/**
	 * 外部循环有什么问题呢？简单来说存在下面三个缺点：

1.只能顺序处理List中的元素（process one by one）

2.不能充分利用多核CPU

3.不利于编译器优化
	 */
	public static void externalIteration(){
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); 
		for (int number : numbers) {  
		    System.out.println(number);  
		}
	}
	
	/**
	 * 内部循环
	 * 这样就能规避上面的三个问题：

1.不一定需要顺序处理List中的元素，顺序可以不确定

2.可以并行处理，充分利用多核CPU的优势

3.有利于JIT编译器对代码进行优化
	 */
	public static void innerIteration(){
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
		numbers.forEach((Integer value) -> System.out.println(value));
	}
	
	//传递行为，而不仅仅是传值
	
	//看一个体现传值局限性的场景吧，上代码
	public int sumAll(List<Integer> numbers){
		int total = 0;  
	    for (int number : numbers) {  
	        total += number;  
	    }  
	    return total; 
	}
	
	/**
	 * sumAll算法很简单，完成的是将List中所有元素相加。
	 * 某一天如果我们需要增加一个对List中所有偶数求和的方法sumAllEven，如下：
	 */
	public int sumAllEven(List<Integer> numbers) {  
	    int total = 0;  
	    for (int number : numbers) {  
	        if (number % 2 == 0) {  
	            total += number;  
	        }  
	    }  
	    return total;  
	} 
	
	/**
	 * 又有一天，我们需要增加第三个方法：对List中所有大于3的元素求和，那是不是继续加下面的方法呢?
	 * @param numbers
	 * @return
	 */
	public int sumAllGt3(List<Integer> numbers) {  
	    int total = 0;  
	    for (int number : numbers) {  
	        if (number > 3) {  
	            total += number;  
	        }  
	    }  
	    return total;  
	}
	/**
	 * 比较这三个方法，我们发现了一个很明显的“代码臭味”—— 代码重复（详情参考《重构》），
	 * 三个方法的唯一区别在于if判断这一行代码。
	 */
	
	
	//Java 8 Lambda表达式中的谓词（Predicate）发挥作用了！
	public int sumAll(List<Integer> numbers, Predicate<Integer> p) {  
	    int total = 0;  
	    for (int number : numbers) {  
	        if (p.test(number)) {  
	            total += number;  
	        }  
	    }  
	    return total;  
	}
	//代码是不是比上面简洁很多了？语义应该也很明确
	//从这里也可以看出未引入Lambda表达式之前的Java代码的冗长（Java这点被很多人诟病）。
	
	
	//4、stream+laziness => efficiency
	//这段代码有什么问题？ 没错，可读性非常差
	public void printNum(){
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
		 
		for (int number : numbers) {  
		    if (number % 2 == 0) {  
		        int n2 = number * 2;  
		        if (n2 > 5) {  
		            System.out.println(n2);  
		            break;  
		        }  
		    }  
		} 
	}
	
	//第一步，我们利用《重构》一书中的最基础的提取小函数重构手法来重构代码如下：
	public static boolean isEven(int number) {  
		System.out.println("isEven:"+number);
	    return number % 2 == 0;  
	}  
	 
	public static int doubleIt(int number) {  
		System.out.println("doubleIt:"+number);
	    return number * 2;  
	}  
	 
	public static boolean isGreaterThan5(int number) {  
		System.out.println("isGreaterThan5:"+number);
	    return number > 5;  
	}  
	
	//OK，代码的意图清晰多了，但是可读性仍然欠佳，因为循环内嵌套一个if分支，if分支内又嵌套另外一个分支
	public void printNum1(){
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
		 
		for (int number : numbers) {  
		    if (isEven(number)) {  
		        int n2 = doubleIt(number);  
		        if (isGreaterThan5(n2)) {  
		            System.out.println(n2);  
		            break;  
		        }  
		    }  
		} 
	}
	
	//于是继续重构代码如下：
	//现在代码够清晰了，这是典型的“流水线”风格代码。
	public void printNum2(){
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
		 
		List<Integer> l1 = new ArrayList<Integer>();  
		for (int n : numbers) {  
		    if (isEven(n)) l1.add(n);  
		}  
		 
		List<Integer> l2 = new ArrayList<Integer>();  
		for (int n : l1) {  
		    l2.add(doubleIt(n));  
		}  
		 
		List<Integer> l3 = new ArrayList<Integer>();  
		for (int n : l2) {  
		    if (isGreaterThan5(n)) l3.add(n);  
		}  
		 
		System.out.println(l3.get(0)); 
	}
	
	/**
	 * 1.代码可读性强

2.代码执行效率不比第一版代码差

3.空间消耗小

Java 8提供了stream方法，我们可以通过对任何集合对象调用stream()方法获得Stream对象，Stream对象有别于Collections的几点如下：

1.不存储值：Streams不会存储值，它们从某个数据结构的流水线型操作中获取值（“酒肉穿肠过”）

2.天生的函数编程特性：对Stream对象操作能得到一个结果，但是不会修改原始数据结构

3.Laziness-seeking（延迟搜索）：Stream的很多操作如filter、map、sort和duplicate removal(去重）可以延迟实现，意思是我们只要检查到满足要求的元素就可以返回

4.可选边界：Streams允许Client取足够多的元素直到满足某个条件为止。而Collections不能这么做
	 */
	public void printNumByStream(){
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
		System.out.println(  
			    numbers.stream()  
			            .filter(LamdbaDemo::isEven)  
			            .map(LamdbaDemo::doubleIt)  
			            .filter(LamdbaDemo::isGreaterThan5)  
			            .findFirst()  
			); 
		
		/**
流程基本和第二版代码一致，这归功于Laziness-seeking特性。怎么理解呢？让我来构造下面这个场景：

Stream流对象要经过下面这种流水线式处理：  
过滤出偶数 => 乘以2 => 过滤出大于5的数 => 取出第一个数  
 
注意：=> 左边的输出是右边的输入 
而Laziness-seeking意味着 我们在每一步只要一找到满足条件的数字，马上传递给下一步去处理并且暂停当前步骤。比如先判断1是否偶数，显然不是；继续判断2是否偶数，是偶数；好，暂停过滤偶数操作，将2传递给下一步乘以2，得到4；4继续传递给第三步，4不满足大于5，所以折回第一步；判断3是否偶数，不是；判断4是否偶数，是偶数；4传递给第二步，乘以2得到8；8传递给第三步，8大于5；所以传递给最后一步，直接取出得到 IntOptional[8]。

IntOptional[8]只是简单包装了下返回的结果，这样有什么好处呢？如果你接触过Null Object Pattern的话就知道了，这样可以避免无谓的null检测。
		 */
	}
	
	

}
